﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace ProjectRoughWork
{
    class Attack : ProjectAIState
    {
        public Attack(SquadActor initActor, GameplayScreen screen, bool initPlayer)
            : base(screen)
        {
            //Initialize data members

            actor = initActor;
            player = initPlayer;
            actor.Moving = false;
            headingForCover = false;
        }

        public override void VOnUpdate()
        {


            if ((target == null) ||
                (target.CurrentHealth <= 0)
                )
            {
                //If there's no target or the target is dead, find a new target
                target = gameScreen.FindClosestEnemy(actor, player);
            }
            else
            {
                //Check if there's a new closer enemy

                SquadActor temp = gameScreen.FindClosestEnemy(actor, player);

                float tempDistance = Math.Abs((temp.getPosition() - actor.getPosition()).Length());
                if (
                    (temp != target) &&
                   (tempDistance < 100)
                   )
                {
                    target = temp;

                    if (actor.Moving)
                    {
                        //If the actor was already heading towards cover or another targt, cancel the movement

                        actor.Moving = false;

                        if (headingForCover)
                        {
                            //Unlock the cover that the actor was heading towards

                            Vector3 coverPos = pfState.CancelPathFind();
                            gameScreen.MakeCoverAvailable(coverPos);
                        }
                    }
                    pfState = null;
                }
            }

            //Call the appropriate attack function based on the unit's type

            if (actor.UnitType.Equals("Soldier"))
            {
                soldierAttack();
            }
            else if (actor.UnitType.Equals("Skirmisher"))
            {
                skirmisherAttack();
            }
            else if (actor.UnitType.Equals("Demolition"))
            {
                demolitionAttack();
            }

            base.VOnUpdate();
        }

        public void soldierAttack()
        {

            //Calculate distance from target

            float targetDistance = (target.getPosition() - actor.getPosition()).Length();

            if ((targetDistance < actor.GunRange) &&
                (!actor.Moving) && 
                (actor.CurrentUnitState != SquadActor.UnitState.Retreating)
                )
            {
                //If the actor is within range to fire his gun, is not already moving and is not retreating
                //  Shoot at the target 

                bool fired = actor.Shoot(target.getOrigin());

                if (!fired)
                {
                    //If the actor didn't fire due to cooldown
                    //  Take the time to check if the Soldier is too close to the target

                    if (gameScreen.CheckIfTooCloseToEnemy(actor.Position, 50, player))
                    {
                        //If too close to the target
                        //  Retreat to a safe distance and update moving and unit's state

                        Queue<Node> path = gameScreen.PathFindToSafety(actor.getPosition(), target.getPosition(), (int)(actor.GunRange * 3 / 4));
                        pfState = new PathFollowing(actor, path);
                        actor.Moving = true;
                        actor.CurrentUnitState = SquadActor.UnitState.Retreating;
                    }
                }
            }
            else 
            {
                //If the soldier is not within range of the target to fire, is already moving or retreating

                if (!actor.Moving)
                {
                   //If the soldier isn't already moving
                    // Path find closer to target

                    if (actor.InCover)
                    {
                        gameScreen.MakeCoverAvailable(actor.getPosition());
                    }

                    Queue<Node> path = gameScreen.PathFindToTarget(actor.getPosition(), target.getPosition(), actor.GunRange - 20);
                    pfState = new PathFollowing(actor, path);
                    actor.Moving = true;
                    headingForCover = false;
                }

                //Update path following algorithm

                pfState.VOnUpdate();

                if (pfState.ReachedEnd)
                {
                    //If the path follow is complete, update state of moving variable

                    actor.Moving = false;

                    if (actor.CurrentUnitState == SquadActor.UnitState.Retreating)
                    {
                        //If the Soldier was retreating, go back to attacking

                        actor.CurrentUnitState = SquadActor.UnitState.Attacking;
                    }
                }
            }
            

        }

        public void skirmisherAttack()
        {
            //Calculate the distance from target

            float targetDistance = (target.getPosition() - actor.getPosition()).Length();

            if ((targetDistance < actor.GunRange) && (!actor.Moving))
            {
                //If the actor is within ragne and not already moving
                //  Fire

                bool fired = actor.Shoot(target.getOrigin());

                if (!fired)
                {
                    //TakeCover? Crouch?...????
                }
            }
            else
            {
                //If not within range or moving

                if (!actor.Moving)
                {
                    //If not already moving, path find to get within range. 

                    if (actor.InCover)
                    {
                        gameScreen.MakeCoverAvailable(actor.getPosition());
                    }

                    Queue<Node> path = gameScreen.PathFindToTarget(actor.getPosition(), target.getPosition(), actor.GunRange - 20);
                    pfState = new PathFollowing(actor, path);
                    actor.Moving = true;
                    headingForCover = false;
                }

                //Update path follow algorithm

                pfState.VOnUpdate();

                if (pfState.ReachedEnd)
                {
                    //If Path follow has reached destination
                    //  Update state of moving variable

                    actor.Moving = false;
                }
            }
        }

        public void demolitionAttack()
        {
            //Calculate the distance from the target

            float targetDistance = (target.getPosition() - actor.getPosition()).Length();
            Demolition tempDemo = ((Demolition)actor);

            if (tempDemo.CurrentAttackState == Demolition.AttackState.Optimal)
            {
                //If the Demo Unit is currently in the Optimal attack state

                if ((targetDistance < actor.GunRange) &&
                    (!actor.Moving) &&
                    (actor.CurrentUnitState != SquadActor.UnitState.Retreating)
                    )
                {
                    //If the Demo unit is within range to fire, not moving and not retreating

                    if ((!actor.InCover) &&
                        (gameScreen.IsCoverAvailable()) &&
                        (!headingForCover)
                        )
                    {
                        //If the Demo unit is not in cover and cover is available
                        //      Path Find to Cover

                        Queue<Node> path = gameScreen.PathFindToCover(actor.getPosition(), Vector3.Zero, 0f);
                        pfState = new PathFollowing(actor, path);
                        actor.Moving = true;
                        headingForCover = true;

                        pfState.VOnUpdate();
                    }
                    else
                    {
                        //If in cover or cover is not available
                        //      Fire at target

                        bool fired = actor.Shoot(target.getOrigin());

                        if (!fired)
                        {
                            //If the Demo unit did not fire due to cooldown
                            //      Check if the actor is too close to the enemy and path find to safety if needed

                            if (gameScreen.CheckIfTooCloseToEnemy(actor.Position, 50, player))
                            {
                                Queue<Node> path = gameScreen.PathFindToSafety(actor.getPosition(), target.getPosition(), 200);
                                pfState = new PathFollowing(actor, path);
                                actor.Moving = true;
                                actor.CurrentUnitState = SquadActor.UnitState.Retreating;
                            }
                        }
                    }


                }
                else
                {

                    //If not within range to attack, already moving or retreating

                    if (!actor.Moving)
                    {

                        //If not already moving
                        //      Attempt to path find to cover

                        Queue<Node> path = gameScreen.PathFindToCover(actor.getPosition(), target.getPosition(), actor.GunRange - 20);
                        headingForCover = true;

                        if (path == null)
                        {
                            //If path find to cover failed
                            //      path find closer to target 

                            path = gameScreen.PathFindToTarget(actor.getPosition(), target.getPosition(), actor.GunRange - 20);
                            tempDemo.CurrentAttackState = Demolition.AttackState.Unoptimal;
                            headingForCover = false;
                        }

                        //If the Demo unit was in cover, release the cover

                        if (actor.InCover)
                        {
                            gameScreen.MakeCoverAvailable(actor.getPosition());
                        }

                        pfState = new PathFollowing(actor, path);
                        actor.Moving = true;
                    }

                    //Update path follow algorithm

                    pfState.VOnUpdate();

                    if (pfState.ReachedEnd)
                    {
                        //If path follow algorithm reached its destination
                        //      update state of moving variable
                        //      Change back to the Attack state if the Demo Unit was retreating

                        actor.Moving = false;
                        if (actor.CurrentUnitState == SquadActor.UnitState.Retreating)
                        {
                            actor.CurrentUnitState = SquadActor.UnitState.Attacking;
                        }
                    } 
                }
            }
            else
            {
                //If the Demo Unit is in the unoptimal state 

                if ( (targetDistance < actor.GunRange) &&
                      (!actor.Moving) &&
                      (actor.CurrentUnitState != SquadActor.UnitState.Retreating)
                    )
                {
                    //If the Demo Unit is within range to fire, not already moving or retreating
                    //      Fire at the target

                    bool fired = actor.Shoot(target.getOrigin());

                    if (fired)
                    {
                        //If the Demo Unit threw the dynamite
                        //      Go back to the optimal attack state
                        //      Check if too close to the enemy and path find to safety if needed

                        tempDemo.CurrentAttackState = Demolition.AttackState.Optimal;
                        if (gameScreen.CheckIfTooCloseToEnemy(actor.Position, 50, player))
                        {
                            Queue<Node> path = gameScreen.PathFindToSafety(actor.getPosition(), target.getPosition(), 200);
                            pfState = new PathFollowing(actor, path);
                            actor.Moving = true;
                            actor.CurrentUnitState = SquadActor.UnitState.Retreating;
                        }
                    }

                }
                else
                {
                    //If not within range, already moving or retreating

                    if (!actor.Moving)
                    {
                        //If not moving already
                        //      Path find closer to target

                        if (actor.InCover)
                        {
                            //If in cover
                            //      Release cover

                            gameScreen.MakeCoverAvailable(actor.getPosition());
                        }

                        Queue<Node> path = gameScreen.PathFindToTarget(actor.getPosition(), target.getPosition(), actor.GunRange - 20);
                        pfState = new PathFollowing(actor, path);
                        actor.Moving = true;
                        headingForCover = false;
                    }

                    //Update Path Follow Algorithm

                    pfState.VOnUpdate();

                    if (pfState.ReachedEnd)
                    {
                        //If Path Follow algorithm has reached destination
                        //      update state of moving variable
                        //      change back to the attacking state if retreating

                        actor.Moving = false;
                        if (actor.CurrentUnitState == SquadActor.UnitState.Retreating)
                        {
                            actor.CurrentUnitState = SquadActor.UnitState.Attacking;
                        }
                    }
                }
            }
            
        }

        //Data Members

        SquadActor actor;               //The actor being influenced
        SquadActor target;              //The current target of the actor being influenced

        bool player;                    //If actor belongs to player squad
        PathFollowing pfState;          //Current PathFollwing algorithm
        bool moving;                    //Whether the actor is moving or not
        bool headingForCover;           //Whether the actor is in cover
    }
}
